﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Scripting;
using Microsoft.Scripting.Ast;
using Microsoft.Scripting.Runtime;
using Microsoft.Linq.Expressions;

using DlrBasic.Runtime;
using DlrBasic.Parser;
using Microsoft.Scripting.Generation;

namespace DlrBasic
{
    /// <summary>
    /// Defines core DlrBasic language contexts. Main class to start any operations.
    /// </summary>
    public class DlrBasicLanguageContext : LanguageContext
    {
        public static readonly SymbolId StatementCallbackName = SymbolTable.StringToId("statementCallback");
        public static readonly SymbolId MemoryName = SymbolTable.StringToId("memory");
        public static readonly SymbolId UserFunctionsName = SymbolTable.StringToId("userFunctions");

        public DlrBasicLanguageContext(ScriptDomainManager manager, IDictionary<string, object> options)
            : base(manager)
        {
            Binder = new DlrBasicBinder(manager);
        }

        public override ScopeExtension CreateScopeExtension(Scope scope)
        {
            return CreateDlrBasicModule(scope);
        }

        public DlrBasicModule CreateDlrBasicModule()
        {
            return CreateDlrBasicModule(new Scope());
        }

        public override string FormatException(Exception exception)
        {
            return exception.GetType().Name + ": " + exception.Message;
        }

        public DlrBasicModule CreateDlrBasicModule(Scope scope)
        {
            DlrBasicModule module = new DlrBasicModule(scope);
            module = (DlrBasicModule) scope.SetExtension(this.ContextId, module);

            if (!module.Scope.ContainsName(StatementCallbackName))
            {
                module.Scope.SetName(StatementCallbackName, new StatementCallback(DefaultStatementCallback));
            }

            if (!module.Scope.ContainsName(MemoryName))
            {
                module.Scope.SetName(MemoryName, new Runtime.DlrBasicMemory());
            }

            if (!module.Scope.ContainsName(UserFunctionsName))
            {
                module.Scope.SetName(UserFunctionsName, new Runtime.DlrBasicUserFunctions());
            }

            return module;
        }

        private static StatementExecutionAction DefaultStatementCallback(CodeContext cotext, DlrBasicState state)
        {
            return StatementExecutionAction.Execute;
        }

        protected override ScriptCode CompileSourceCode(SourceUnit sourceUnit, CompilerOptions options, ErrorSink errorSink)
        {
            DlrBasicParser parser = new DlrBasicParser(sourceUnit);

            DlrBasic.Parser.Ast.DlrBasicNode parsed;
            LambdaExpression expression;

            switch (sourceUnit.Kind)
            {
                case SourceCodeKind.InteractiveCode:
                    parsed = parser.ParseInteractiveStatement();
                    expression = DlrBasicGenerator.Generate(this, parsed, sourceUnit);
                    break;
                default:
                    parsed = parser.ParseFile();
                    expression = DlrBasicGenerator.Generate(this, parsed, sourceUnit);
                    break;
            }

            expression = new GlobalLookupRewriter().RewriteLambda(expression);
            return new ScriptCode(expression, sourceUnit);
        }
    }
}
